index.js ➔ sign   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 22
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 19
dl 0
loc 22
ccs 18
cts 18
cp 1
crap 3
rs 9.45
c 0
b 0
f 0
1
/**
2
 * @license
3
 * Copyright (c) 2020 UMI
4
 *
5
 * Permission is hereby granted, free of charge, to any person obtaining a copy
6
 * of this software and associated documentation files (the "Software"), to deal
7
 * in the Software without restriction, including without limitation the rights
8
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
 * copies of the Software, and to permit persons to whom the Software is
10
 * furnished to do so, subject to the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be included in all
13
 * copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
 * SOFTWARE.
22
 */
23
24
'use strict'
25
26 1
const sha512 = require('../sha512.js')
27 1
const array = require('../array.js')
28 1
const common = require('./common.js')
29
30 1
const D = [
31
  0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070,
32
  0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203
33
]
34 1
const I = [
35
  0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43,
36
  0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83
37
]
38
/**
39
 * @param {ArrayLike<number>} seed
40
 * @returns {number[]}
41
 * @private
42
 */
43
function secretKeyFromSeed (seed) {
44 5
  const pk = []
45 5
  const p = [[], [], [], []]
46 5
  const d = sha512.sha512(seed)
47 5
  d[0] &= 248
48 5
  d[31] &= 127
49 5
  d[31] |= 64
50 5
  common.scalarbase(p, d)
51 5
  common.pack(pk, p)
52 5
  return array.arrayConcat(seed, pk)
53
}
54
/**
55
 * @param {ArrayLike<number>} message
56
 * @param {ArrayLike<number>} secretKey
57
 * @returns {number[]}
58
 * @private
59
 */
60
function sign (message, secretKey) {
61 2
  const d = sha512.sha512(array.arraySlice(secretKey, 0, 32))
62 2
  d[0] &= 248
63 2
  d[31] &= 127
64 2
  d[31] |= 64
65 2
  const sm = d.slice(0)
66 2
  array.arraySet(sm, message, 64)
67 2
  const r = sha512.sha512(sm.slice(32))
68 2
  common.reduce(r)
69 2
  const p = [[], [], [], []]
70 2
  common.scalarbase(p, r)
71 2
  common.pack(sm, p)
72 2
  array.arraySet(sm, array.arraySlice(secretKey, 32), 32)
73 2
  const h = sha512.sha512(sm)
74 2
  common.reduce(h)
75 2
  for (let i = 0; i < 32; i++) {
76 64
    for (let j = 0; j < 32; j++) {
77 2048
      r[i + j] += h[i] * d[j]
78
    }
79
  }
80 2
  return array.arrayConcat(sm.slice(0, 32), common.modL(sm.slice(32), r).slice(0, 32))
81
}
82
/**
83
 * @param {ArrayLike<number>} signature
84
 * @param {ArrayLike<number>} message
85
 * @param {ArrayLike<number>} pubKey
86
 * @returns {boolean}
87
 * @private
88
 */
89
function verify (signature, message, pubKey) {
90 3
  const sm = []
91 3
  const t = []
92 3
  const p = [[], [], [], []]
93 3
  const q = [[], [], [], []]
94
  /* istanbul ignore if */
95 3
  if (!unpackneg(q, array.arraySlice(pubKey))) {
96
    return false
97
  }
98 3
  array.arraySet(sm, signature, 0)
99 3
  array.arraySet(sm, message, 64)
100 3
  const m = sm.slice(0)
101 3
  array.arraySet(m, pubKey, 32)
102 3
  const h = sha512.sha512(m)
103 3
  common.reduce(h)
104 3
  common.scalarmult(p, q, h)
105 3
  common.scalarbase(q, sm.slice(32))
106 3
  common.add(p, q)
107 3
  common.pack(t, p)
108 3
  return cryptoVerify32(sm, t)
109
}
110
/**
111
 * @param {number[][]} r
112
 * @param {number[]} p
113
 * @returns {boolean}
114
 * @private
115
 */
116
function unpackneg (r, p) {
117 3
  const t = []
118 3
  const chk = []
119 3
  const num = []
120 3
  const den = []
121 3
  const den2 = []
122 3
  const den4 = []
123 3
  const den6 = []
124 3
  array.arraySet(r[2], common.gf1)
125 3
  unpack25519(r[1], p)
126 3
  common.fnM(num, r[1], r[1])
127 3
  common.fnM(den, num, D)
128 3
  common.fnZ(num, num, r[2])
129 3
  common.fnA(den, r[2], den)
130 3
  common.fnM(den2, den, den)
131 3
  common.fnM(den4, den2, den2)
132 3
  common.fnM(den6, den4, den2)
133 3
  common.fnM(t, den6, num)
134 3
  common.fnM(t, t, den)
135 3
  pow2523(t, t)
136 3
  common.fnM(t, t, num)
137 3
  common.fnM(t, t, den)
138 3
  common.fnM(t, t, den)
139 3
  common.fnM(r[0], t, den)
140 3
  common.fnM(chk, r[0], r[0])
141 3
  common.fnM(chk, chk, den)
142 3
  if (!neq25519(chk, num)) {
143 1
    common.fnM(r[0], r[0], I)
144
  }
145 3
  common.fnM(chk, r[0], r[0])
146 3
  common.fnM(chk, chk, den)
147
  /* istanbul ignore if */
148 3
  if (!neq25519(chk, num)) {
149
    return false
150
  }
151 3
  if (common.par25519(r[0]) === (p[31] >> 7)) {
152 1
    common.fnZ(r[0], common.gf0, r[0])
153
  }
154 3
  common.fnM(r[3], r[0], r[1])
155 3
  return true
156
}
157
/**
158
 * @param {number[]} x
159
 * @param {number[]} y
160
 * @returns {boolean}
161
 * @private
162
 */
163
function cryptoVerify32 (x, y) {
164 9
  let d = 0
165 9
  for (let i = 0; i < 32; i++) {
166 288
    d |= x[i] ^ y[i]
167
  }
168 9
  return (1 & ((d - 1) >>> 8)) === 1
169
}
170
/**
171
 * @param {number[]} o
172
 * @param {number[]} n
173
 * @private
174
 */
175
function unpack25519 (o, n) {
176 3
  for (let i = 0; i < 16; i++) {
177 48
    o[i] = n[2 * i] + (n[2 * i + 1] << 8)
178
  }
179 3
  o[15] &= 0x7fff
180
}
181
/**
182
 * @param {number[]} o
183
 * @param {number[]} i
184
 * @private
185
 */
186
function pow2523 (o, i) {
187 3
  const c = []
188
  let a
189 3
  for (a = 0; a < 16; a++) {
190 48
    c[a] = i[a]
191
  }
192 3
  for (a = 250; a >= 0; a--) {
193 753
    common.fnM(c, c, c)
194 753
    if (a !== 1) {
195 750
      common.fnM(c, c, i)
196
    }
197
  }
198 3
  for (a = 0; a < 16; a++) {
199 48
    o[a] = c[a]
200
  }
201
}
202
/**
203
 * @param {number[]} a
204
 * @param {number[]} b
205
 * @throws {boolean}
206
 * @private
207
 */
208
function neq25519 (a, b) {
209 6
  const c = []
210 6
  const d = []
211 6
  common.pack25519(c, a)
212 6
  common.pack25519(d, b)
213 6
  return cryptoVerify32(c, d)
214
}
215
216 1
exports.secretKeyFromSeed = secretKeyFromSeed
217 1
exports.sign = sign
218
exports.verify = verify
219